Packages
library(tidyverse)
library(nlme)
library(plotly)
Importing Dataset
setwd("~/Insync/leonardogloria@uenf.br/Google Drive/MENTORIA_R/Monday_meeting")
dadosg <- read.table("data_growth.txt", h = T,na.strings=".") %>%
mutate(age=idade,w=peso,animf=factor(animal),class=factor(sexo)) %>%
groupedData(w~age|animf,data=.) #Creating dataset indicating animal repeated measures by age
Logistic model, without fixed effect
teta=c(); AIcc=c();
model00=nlme(w~a/(1+b*(exp(-k*age))),fixed=list(a+b+k~1),random=pdDiag(a~1),
control=nlmeControl(maxIter=100),
data=dadosg,
start=list(fixed =c(30.4443882,12.7867281,0.06)),na.action=na.omit)
(teta[1]=attributes(logLik(model00))$df)
(AIcc[1]=-2*model00$logLik+2*teta[1]+(2*teta[1]*(teta[1]+1))/(nrow(dadosg)-teta[1]-1))
Logistic model with class of fixed effect, in other words, we will fit a parameter for each sex
st01=c(rep(summary(model00)$ coefficients$fixed[[1]],length(levels(dadosg$class))),rep(summary(model00)$ coefficients$fixed[[2]],length(levels(dadosg$class))),rep(summary(model00)$ coefficients$fixed[[3]],length(levels(dadosg$class)))) #starting values based on the model00 output
model01=nlme(w~a/(1+b*(exp(-k*age))),fixed=list(a+b+k~class-1),
random=pdDiag(a+b+k~1),control=nlmeControl(maxIter=500),
data=dadosg,start=st01,na.action=na.omit)
(teta[2]=attributes(logLik(model01))$df)
(AIcc[2]=-2*model01$logLik+2*teta[2]+(2*teta[2]*(teta[2]+1))/(nrow(dadosg)-teta[2]-1))
Logistic model with class of fixed effect, in other words, we will fit a parameter for each sex, plus (weights = heterogeneous variance along the time, varPower= power variance function is defined as s2(v) = |v|^(2*t))
parf=c()
for (i in 1:length(summary(model01)$ coefficients$fixed)) {
par1=summary(model01)$ coefficients$fixed[[i]]
parf=rbind(parf,par1)
} #starting values based on the model01 output, each class has its own start value
model02=nlme(w~a/(1+b*(exp(-k*age))),fixed=list(a+b+k~class-1),
random=pdDiag(a+b+k~1),control=nlmeControl(maxIter=500),
data=dadosg,start=parf,na.action=na.omit,weights= varPower())
intervals(model02)
######################
(teta[3]=attributes(logLik(model02))$df)
(AIcc[3]=-2*model02$logLik+2*teta[3]+(2*teta[3]*(teta[3]+1))/(nrow(dadosg)-teta[3]-1))
Logistic model with class of fixed effect, in other words, we will fit a parameter for each sex, plus (corr = modeling animal repeated measures, CAR1 = continous autoregressive)
parf=c()
for (i in 1:length(summary(model01)$ coefficients$fixed)) {
par1=summary(model01)$ coefficients$fixed[[i]]
parf=rbind(parf,par1)
} #starting values based on the model01 output, each class has its own start value
model03=nlme(w~a/(1+b*(exp(-k*age))),fixed=list(a+b+k~class-1),
random=pdDiag(a+b+k~1),control=nlmeControl(maxIter=100),
data=dadosg,start=parf,na.action=na.omit,corr=corCAR1())
######################
(teta[4]=attributes(logLik(model03))$df)
(AIcc[4]=-2*model03$logLik+2*teta[4]+(2*teta[4]*(teta[4]+1))/(nrow(dadosg)-teta[4]-1))
Full Logistic model with class of fixed effect, heterogeneous variance along the time, repeated measures
parf=c()
for (i in 1:length(summary(model03)$ coefficients$fixed)) {
par1=summary(model03)$ coefficients$fixed[[i]]
parf=rbind(parf,par1)
}
model04=nlme(w~a/(1+b*(exp(-k*age))),fixed=list(a+b+k~class-1),
random=pdDiag(a~1),control=nlmeControl(minScale=10**-100,maxIter=500),
data=dadosg,start=parf,na.action=na.omit,corr=corCAR1(),weights= varPower()) #logistico
######################
(teta[5]=attributes(logLik(model04))$df)
(AIcc[5]=-2*model04$logLik+2*teta[5]+(2*teta[5]*(teta[5]+1))/(nrow(dadosg)-teta[5]-1))
Gompertz model, without fixed effect
model05=nlme(w~a*exp(-b*(exp(-k*age))),fixed=list(a+b+k~1),random=pdDiag(a~1),
control=nlmeControl(maxIter=100),
data=dadosg,
start=list(fixed =c(30.4443882,12.7867281,0.06)),na.action=na.omit)
(teta[6]=attributes(logLik(model05))$df)
(AIcc[6]=-2*model05$logLik+2*teta[6]+(2*teta[6]*(teta[6]+1))/(nrow(dadosg)-teta[6]-1))
Gompertz model with class of fixed effect, in other words, we will fit a parameter for each sex
st05=c(rep(summary(model05)$ coefficients$fixed[[1]],length(levels(dadosg$class))),rep(summary(model05)$ coefficients$fixed[[2]],length(levels(dadosg$class))),rep(summary(model05)$ coefficients$fixed[[3]],length(levels(dadosg$class)))) #starting values based on the model05 output
model06=nlme(w~a*exp(-b*(exp(-k*age))),fixed=list(a+b+k~class-1),
random=pdDiag(a+b+k~1),control=nlmeControl(maxIter=100),
data=dadosg,start=st01,na.action=na.omit)
(teta[7]=attributes(logLik(model06))$df)
(AIcc[7]=-2*model06$logLik+2*teta[7]+(2*teta[7]*(teta[7]+1))/(nrow(dadosg)-teta[7]-1))
Gompertz model with class of fixed effect, in other words, we will fit a parameter for each sex, plus (weights = heterogeneous variance along the time, varPower= power variance function is defined as s2(v) = |v|^(2*t))
parf=c()
for (i in 1:length(summary(model06)$ coefficients$fixed)) {
par1=summary(model06)$ coefficients$fixed[[i]]
parf=rbind(parf,par1)
} #starting values based on the model01 output, each class has its own start value
model07=nlme(w~a*exp(-b*(exp(-k*age))),fixed=list(a+b+k~class-1),
random=pdDiag(a+b+k~1),control=nlmeControl(maxIter=100),
data=dadosg,start=parf,na.action=na.omit,weights= varPower())
intervals(model07)
######################
(teta[8]=attributes(logLik(model07))$df)
(AIcc[8]=-2*model07$logLik+2*teta[8]+(2*teta[8]*(teta[8]+1))/(nrow(dadosg)-teta[8]-1))
Gompertz model with class of fixed effect, in other words, we will fit a parameter for each sex, plus (corr = modeling animal repeated measures, CAR1 = continous autoregressive)
parf=c()
for (i in 1:length(summary(model06)$ coefficients$fixed)) {
par1=summary(model06)$ coefficients$fixed[[i]]
parf=rbind(parf,par1)
} #starting values based on the model01 output, each class has its own start value
model08=nlme(w~a*exp(-b*(exp(-k*age))),fixed=list(a+b+k~class-1),
random=pdDiag(a+b+k~1),control=nlmeControl(maxIter=100),
data=dadosg,start=parf,na.action=na.omit,corr=corCAR1())
######################
(teta[9]=attributes(logLik(model08))$df)
(AIcc[9]=-2*model08$logLik+2*teta[9]+(2*teta[9]*(teta[9]+1))/(nrow(dadosg)-teta[9]-1))
Full Gompertz model with class of fixed effect, heterogeneous variance along the time, repeated measures
parf=c()
for (i in 1:length(summary(model06)$ coefficients$fixed)) {
par1=summary(model06)$ coefficients$fixed[[i]]
parf=rbind(parf,par1)
} #starting values based on the model01 output, each class has its own start value
model09=nlme(w~a*exp(-b*(exp(-k*age))),fixed=list(a+b+k~class-1),
random=pdDiag(a+k~1),control=nlmeControl(minScale=10**-100,maxIter=500),
data=dadosg,start=parf,na.action=na.omit,corr=corCAR1(),weights= varPower())
######################
(teta[10]=attributes(logLik(model09))$df)
(AIcc[10]=-2*model09$logLik+2*teta[10]+(2*teta[10]*(teta[10]+1))/(nrow(dadosg)-teta[10]-1))
multi-model selection framework (Burnham and Anderson, 2004)
delta=c()
for(i in 1: length(AIcc)){
delta[i]=AIcc[i]-min(AIcc)
}
wpro=c()
for(i in 1:length(AIcc)){
wpro[i]=exp(-delta[i]/2)
}
sum(wpro[1:length(AIcc)])
wprob=c()
for(i in 1: length(AIcc)){
wprob[i]=exp(-delta[i]/2)/sum(wpro[1:length(AIcc)])
}
ER=c()
for(i in 1: length(AIcc)){
ER[i]=max(wprob)/wprob[i]
}
(quadro.akaike=data.frame(teta,AICc=AIcc,delta,wprob,ER))
Prediction and Growth rate function
Gompertzhat=function(a,b,k,age){
y=a*exp(-b*(exp(-k*age)))
return(y)
}
GRGompertz=function(a,b,k,age){
y=a * (exp(-b * (exp(-k * age))) * (b * (exp(-k * age) * k)))
return(y)
}
creating interactive graphics with plotly
setwd("~/Insync/leonardogloria@uenf.br/Google Drive/MENTORIA_R/Monday_meeting")
param=read.table("parameters.txt",h=T)
row.names(param)=c("Males","Females")
time=seq(1,90,by=0.25)
#predictions
predc1=Gompertzhat(a=param[1,1],b=param[1,2],k=param[1,3],age =time)
predc2=Gompertzhat(a=param[2,1],b=param[2,2],k=param[2,3],age =time)
#growth rate
grc1=GRGompertz(a=param[1,1],b=param[1,2],k=param[1,3],age =time)
grc2=GRGompertz(a=param[2,1],b=param[2,2],k=param[2,3],age =time)
#inflaction point (max efficiency)
maxef1=data.frame(grc1)
rownames(maxef1)=time
maxefx1=as.numeric(rownames(maxef1)[which.max(maxef1$grc1)])
maxefy1=maxef1$grc1[which.max(maxef1$grc1)]
maxef2=data.frame(grc2)
rownames(maxef2)=time
maxefx2=as.numeric(rownames(maxef2)[which.max(maxef2$grc2)])
maxefy2=maxef2$grc2[which.max(maxef2$grc2)]
### Multiple Y Axes
ay <- list(
tickfont = list(color = "black"),
overlaying = "y",
side = "right",
title = "Daily Weight Gain(g)"
)
fig <- plot_ly()
fig <- fig %>% add_lines(x = time, y = predc1, name = "Weight Males")
fig <- fig %>% add_lines(x = time, y = grc1, name = "Growth Rate Males", yaxis = "y2")
fig <- fig %>% add_markers(x = maxefx1, y = maxefy1, name = "Max Growth Rate Males", yaxis = "y2")
fig <- fig %>% add_segments(line=list(type="dash"),x = maxefx1,xend=maxefx1, y = 0,yend =maxefy1, yaxis = "y2",name = "Max Growth Rate Males")
fig <- fig %>% add_lines(x = time, y = predc2, name = "Weight Females")
fig <- fig %>% add_lines(x = time, y = grc2, name = "Growth Rate Females", yaxis = "y2")
fig <- fig %>% add_markers(x = maxefx2, y = maxefy2, name = "Max Growth Rate Females", yaxis = "y2")
fig <- fig %>% add_segments(color = I("gray"),x = maxefx2,xend=maxefx2, y = 0,yend =maxefy2, yaxis = "y2",name = "Max Growth Rate Females")
fig <- fig %>% layout(
title = "Growth Curve with Growth rate", yaxis2 = ay,
xaxis = list(title="Age(days)"),
yaxis = list(title="Weight(g)")
)
fig
Importing Milking Dataset
Importing dataset from txt
setwd("~/Insync/leonardogloria@uenf.br/Google Drive/MENTORIA_R/Monday_meeting")
data_milkg <- read.table("data_milk.txt", h = T) %>%
mutate(animal=factor(animal),CG=factor(CG)) %>%
groupedData(MY~DIM|animal,data=.) #Creating dataset indicating animal repeated measures by days in milk
WOOD model, without fixed effect
teta=c(); AIcc=c();
model00=nlme(MY~a*(DIM**b)*exp(-k*DIM),fixed=list(a+b+k~1),random=pdDiag(a+b~1),
control=nlmeControl(maxIter=100),
data=data_milkg,
start=list(fixed =c(20,0.1,0.003)),na.action=na.omit)
(teta[1]=attributes(logLik(model00))$df)
[1] 6
(AIcc[1]=-2*model00$logLik+2*teta[1]+(2*teta[1]*(teta[1]+1))/(nrow(data_milkg)-teta[1]-1))
[1] 13428.63
WOOD model with class of fixed effect, in other words, we will fit a parameter for each sex
st01=c(rep(summary(model00)$ coefficients$fixed[[1]],length(levels(data_milkg$CG))),rep(summary(model00)$ coefficients$fixed[[2]],length(levels(data_milkg$CG))),rep(summary(model00)$ coefficients$fixed[[3]],length(levels(data_milkg$CG)))) #starting values based on the model00 output
model01=nlme(MY~a*(DIM**b)*exp(-k*DIM),fixed=list(a+b+k~CG-1),random=pdDiag(a~1),
control=nlmeControl(maxIter=100),
data=data_milkg,start=st01,na.action=na.omit)
(teta[2]=attributes(logLik(model01))$df)
[1] 20
(AIcc[2]=-2*model01$logLik+2*teta[2]+(2*teta[2]*(teta[2]+1))/(nrow(data_milkg)-teta[2]-1))
[1] 13355.63
WOOD model with class of fixed effect, in other words, we will fit a parameter for each sex, plus (weights = heterogeneous variance along the time, varPower= power variance function is defined as s2(v) = |v|^(2*t))
parf=c()
for (i in 1:length(summary(model01)$ coefficients$fixed)) {
par1=summary(model01)$ coefficients$fixed[[i]]
parf=rbind(parf,par1)
} #starting values based on the model01 output, each class has its own start value
model02=nlme(MY~a*(DIM**b)*exp(-k*DIM),fixed=list(a+b+k~CG-1),random=pdDiag(a~1),
control=nlmeControl(maxIter=500),
data=data_milkg,start=parf,na.action=na.omit,weights= varPower())
intervals(model02)
######################
(teta[3]=attributes(logLik(model02))$df)
(AIcc[3]=-2*model02$logLik+2*teta[3]+(2*teta[3]*(teta[3]+1))/(nrow(data_milkg)-teta[3]-1))
WOOD model with class of fixed effect, in other words, we will fit a parameter for each sex, plus (corr = modeling animal repeated measures, CAR1 = continous autoregressive)
parf=c()
for (i in 1:length(summary(model01)$ coefficients$fixed)) {
par1=summary(model01)$ coefficients$fixed[[i]]
parf=rbind(parf,par1)
} #starting values based on the model01 output, each class has its own start value
model03=nlme(MY~a*(DIM**b)*exp(-k*DIM),fixed=list(a+b+k~CG-1),random=pdDiag(a~1),
control=nlmeControl(maxIter=100),
data=data_milkg,start=parf,na.action=na.omit,corr=corCAR1())
######################
(teta[4]=attributes(logLik(model03))$df)
[1] 21
(AIcc[4]=-2*model03$logLik+2*teta[4]+(2*teta[4]*(teta[4]+1))/(nrow(data_milkg)-teta[4]-1))
[1] 12224.5
Full WOOD model with class of fixed effect, heterogeneous variance along the time, repeated measures
parf=c()
for (i in 1:length(summary(model03)$ coefficients$fixed)) {
par1=summary(model03)$ coefficients$fixed[[i]]
parf=rbind(parf,par1)
}
model04=nlme(MY~a*(DIM**b)*exp(-k*DIM),fixed=list(a+b+k~CG-1),random=pdDiag(a+b+k~1),
control=nlmeControl(maxIter=100),
data=data_milkg,start=parf,na.action=na.omit,corr=corCAR1(),weights= varPower())
parf=c()
for (i in 1:length(summary(model03)$ coefficients$fixed)) {
par1=summary(model03)$ coefficients$fixed[[i]]
parf=rbind(parf,par1)
}
model04=nlme(MY~a*(DIM**b)*exp(-k*DIM),fixed=list(a+b+k~CG-1),random=pdDiag(a+b+k~1),
control=nlmeControl(maxIter=100),
data=data_milkg,start=parf,na.action=na.omit,corr=corCAR1(),weights= varPower())
parf=c()
for (i in 1:length(summary(model03)$ coefficients$fixed)) {
par1=summary(model03)$ coefficients$fixed[[i]]
parf=rbind(parf,par1)
}
model04=nlme(MY~a*(DIM**b)*exp(-k*DIM),fixed=list(a+b+k~CG-1),random=pdDiag(a+b+k~1),
control=nlmeControl(maxIter=100),
data=data_milkg,start=parf,na.action=na.omit,corr=corCAR1(),weights= varPower())
parf=c()
for (i in 1:length(summary(model03)$ coefficients$fixed)) {
par1=summary(model03)$ coefficients$fixed[[i]]
parf=rbind(parf,par1)
}
model04=nlme(MY~a*(DIM**b)*exp(-k*DIM),fixed=list(a+b+k~CG-1),random=pdDiag(a+b+k~1),
control=nlmeControl(maxIter=100),
data=data_milkg,start=parf,na.action=na.omit,corr=corCAR1(),weights= varPower())
parf=c()
for (i in 1:length(summary(model03)$ coefficients$fixed)) {
par1=summary(model03)$ coefficients$fixed[[i]]
parf=rbind(parf,par1)
}
model04=nlme(MY~a*(DIM**b)*exp(-k*DIM),fixed=list(a+b+k~CG-1),random=pdDiag(a+b+k~1),
control=nlmeControl(maxIter=100),
data=data_milkg,start=parf,na.action=na.omit,corr=corCAR1(),weights= varPower())
parf=c()
for (i in 1:length(summary(model03)$ coefficients$fixed)) {
par1=summary(model03)$ coefficients$fixed[[i]]
parf=rbind(parf,par1)
}
model04=nlme(MY~a*(DIM**b)*exp(-k*DIM),fixed=list(a+b+k~CG-1),random=pdDiag(a+b+k~1),
control=nlmeControl(maxIter=100),
data=data_milkg,start=parf,na.action=na.omit,corr=corCAR1(),weights= varPower())
multi-model selection framework (Burnham and Anderson, 2004)
delta=c()
for(i in 1: length(AIcc)){
delta[i]=AIcc[i]-min(AIcc)
}
wpro=c()
for(i in 1:length(AIcc)){
wpro[i]=exp(-delta[i]/2)
}
sum(wpro[1:length(AIcc)])
[1] 1
wprob=c()
for(i in 1: length(AIcc)){
wprob[i]=exp(-delta[i]/2)/sum(wpro[1:length(AIcc)])
}
ER=c()
for(i in 1: length(AIcc)){
ER[i]=max(wprob)/wprob[i]
}
(quadro.akaike=data.frame(teta,AICc=AIcc,delta,wprob,ER))
Prediction MY and TMY
MWoodhat=function(a,b,k,age){
y=(a*age^b)*exp(-k*age)
return(y)
}
Total Milk Yield
(TMYCG22=MYWood(a=as.numeric(intervals(model04)$fixed[2,2]),
b=as.numeric(intervals(model04)$fixed[8,2]),
k=as.numeric(intervals(model04)$fixed[14,2]),
MYl=10,MYu=305))
value
[1,] 6900.513
(TMYCG32=MYWood(a=as.numeric(intervals(model04)$fixed[5,2]),
b=as.numeric(intervals(model04)$fixed[11,2]),
k=as.numeric(intervals(model04)$fixed[17,2]),
MYl=10,MYu=305))
value
[1,] 6739.791
TMYCG22[[1]]-TMYCG32[[1]]
[1] 160.7221
creating interactive graphics with plotly
age=seq(1,305,by=0.25)
#predictions
predc1=MWoodhat(a=as.numeric(intervals(model04)$fixed[2,2]),b=as.numeric(intervals(model04)$fixed[8,2]),k=as.numeric(intervals(model04)$fixed[14,2]),age=age)
predc2=MWoodhat(a=as.numeric(intervals(model04)$fixed[5,2]),b=as.numeric(intervals(model04)$fixed[11,2]),k=as.numeric(intervals(model04)$fixed[17,2]),age=age)
### Multiple Y Axes
#ay <- list(
# tickfont = list(color = "black"),
# overlaying = "y",
# side = "right",
# title = "Daily Weight Gain(g)"
#)
fig <- plot_ly()
fig <- fig %>% add_lines(x = age, y = predc1, name = "Milk Yield CG22")
fig <- fig %>% add_lines(x = age, y = predc2, name = "Milk Yield CG32")
fig <- fig %>% layout(
title = "WOOD Milk curve",
xaxis = list(title="DIM(days)"),
yaxis = list(title="MY (kg)")
)
fig
LS0tCnRpdGxlOiAiTm9uLUxpbmVhciBNaXhlZCBNb2RlbHMiCmF1dGhvcjogIkJyaXRvJ3MgTGFiIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKaGlnaGxpZ2h0OiB0YW5nbwotLS0KCjxocj4KCmBgYHtyIGVjaG8gPSBGQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGluY2x1ZGUgPSBULCBlY2hvID1ULCBtZXNzYWdlPUYsIHdhcm5pbmc9RikKYGBgCgojIyMjIFBhY2thZ2VzCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobmxtZSkKbGlicmFyeShwbG90bHkpCmBgYAoKIyMjIyMgSW1wb3J0aW5nIERhdGFzZXQKCmBgYHtyfQpzZXR3ZCgifi9JbnN5bmMvbGVvbmFyZG9nbG9yaWFAdWVuZi5ici9Hb29nbGUgRHJpdmUvTUVOVE9SSUFfUi9Nb25kYXlfbWVldGluZyIpCmRhZG9zZyA8LSByZWFkLnRhYmxlKCJkYXRhX2dyb3d0aC50eHQiLCBoID0gVCxuYS5zdHJpbmdzPSIuIikgJT4lIAogIG11dGF0ZShhZ2U9aWRhZGUsdz1wZXNvLGFuaW1mPWZhY3RvcihhbmltYWwpLGNsYXNzPWZhY3RvcihzZXhvKSkgJT4lIAogIGdyb3VwZWREYXRhKHd+YWdlfGFuaW1mLGRhdGE9LikgI0NyZWF0aW5nIGRhdGFzZXQgaW5kaWNhdGluZyBhbmltYWwgcmVwZWF0ZWQgbWVhc3VyZXMgYnkgYWdlCgpgYGAKCiMjIyMjIExvZ2lzdGljIG1vZGVsLCB3aXRob3V0IGZpeGVkIGVmZmVjdApgYGB7cn0KdGV0YT1jKCk7ICAgQUljYz1jKCk7Cgptb2RlbDAwPW5sbWUod35hLygxK2IqKGV4cCgtayphZ2UpKSksZml4ZWQ9bGlzdChhK2Ira34xKSxyYW5kb209cGREaWFnKGF+MSksCiAgICAgICAgICAgICBjb250cm9sPW5sbWVDb250cm9sKG1heEl0ZXI9MTAwKSwKICAgICAgICAgICAgIGRhdGE9ZGFkb3NnLAogICAgICAgICAgICAgc3RhcnQ9bGlzdChmaXhlZCA9YygzMC40NDQzODgyLDEyLjc4NjcyODEsMC4wNikpLG5hLmFjdGlvbj1uYS5vbWl0KSAgICAgICAKCih0ZXRhWzFdPWF0dHJpYnV0ZXMobG9nTGlrKG1vZGVsMDApKSRkZikKKEFJY2NbMV09LTIqbW9kZWwwMCRsb2dMaWsrMip0ZXRhWzFdKygyKnRldGFbMV0qKHRldGFbMV0rMSkpLyhucm93KGRhZG9zZyktdGV0YVsxXS0xKSkKYGBgCgojIyMjIyAgTG9naXN0aWMgbW9kZWwgd2l0aCBjbGFzcyBvZiBmaXhlZCBlZmZlY3QsIGluIG90aGVyIHdvcmRzLCB3ZSB3aWxsIGZpdCBhIHBhcmFtZXRlciBmb3IgZWFjaCBzZXgKYGBge3J9CnN0MDE9YyhyZXAoc3VtbWFyeShtb2RlbDAwKSQgY29lZmZpY2llbnRzJGZpeGVkW1sxXV0sbGVuZ3RoKGxldmVscyhkYWRvc2ckY2xhc3MpKSkscmVwKHN1bW1hcnkobW9kZWwwMCkkIGNvZWZmaWNpZW50cyRmaXhlZFtbMl1dLGxlbmd0aChsZXZlbHMoZGFkb3NnJGNsYXNzKSkpLHJlcChzdW1tYXJ5KG1vZGVsMDApJCBjb2VmZmljaWVudHMkZml4ZWRbWzNdXSxsZW5ndGgobGV2ZWxzKGRhZG9zZyRjbGFzcykpKSkgI3N0YXJ0aW5nIHZhbHVlcyBiYXNlZCBvbiB0aGUgbW9kZWwwMCBvdXRwdXQKCm1vZGVsMDE9bmxtZSh3fmEvKDErYiooZXhwKC1rKmFnZSkpKSxmaXhlZD1saXN0KGErYitrfmNsYXNzLTEpLAogICAgICAgICAgICAgcmFuZG9tPXBkRGlhZyhhK2Ira34xKSxjb250cm9sPW5sbWVDb250cm9sKG1heEl0ZXI9NTAwKSwKICAgICAgICAgICAgIGRhdGE9ZGFkb3NnLHN0YXJ0PXN0MDEsbmEuYWN0aW9uPW5hLm9taXQpCgoKKHRldGFbMl09YXR0cmlidXRlcyhsb2dMaWsobW9kZWwwMSkpJGRmKQooQUljY1syXT0tMiptb2RlbDAxJGxvZ0xpaysyKnRldGFbMl0rKDIqdGV0YVsyXSoodGV0YVsyXSsxKSkvKG5yb3coZGFkb3NnKS10ZXRhWzJdLTEpKQpgYGAKCiMjIyMjICBMb2dpc3RpYyBtb2RlbCB3aXRoIGNsYXNzIG9mIGZpeGVkIGVmZmVjdCwgaW4gb3RoZXIgd29yZHMsIHdlIHdpbGwgZml0IGEgcGFyYW1ldGVyIGZvciBlYWNoIHNleCwgcGx1cyAod2VpZ2h0cyA9IGhldGVyb2dlbmVvdXMgdmFyaWFuY2UgYWxvbmcgdGhlIHRpbWUsIHZhclBvd2VyPSBwb3dlciB2YXJpYW5jZSBmdW5jdGlvbiBpcyBkZWZpbmVkIGFzIHMyKHYpID0gfHZ8XigyKnQpKQoKYGBge3J9CnBhcmY9YygpCmZvciAoaSBpbiAxOmxlbmd0aChzdW1tYXJ5KG1vZGVsMDEpJCBjb2VmZmljaWVudHMkZml4ZWQpKSAgICAgIHsKcGFyMT1zdW1tYXJ5KG1vZGVsMDEpJCBjb2VmZmljaWVudHMkZml4ZWRbW2ldXQpwYXJmPXJiaW5kKHBhcmYscGFyMSkKIH0gI3N0YXJ0aW5nIHZhbHVlcyBiYXNlZCBvbiB0aGUgbW9kZWwwMSBvdXRwdXQsIGVhY2ggY2xhc3MgaGFzIGl0cyBvd24gc3RhcnQgdmFsdWUKCm1vZGVsMDI9bmxtZSh3fmEvKDErYiooZXhwKC1rKmFnZSkpKSxmaXhlZD1saXN0KGErYitrfmNsYXNzLTEpLAogICAgICAgICAgICAgcmFuZG9tPXBkRGlhZyhhK2Ira34xKSxjb250cm9sPW5sbWVDb250cm9sKG1heEl0ZXI9NTAwKSwKICAgICAgICAgICAgIGRhdGE9ZGFkb3NnLHN0YXJ0PXBhcmYsbmEuYWN0aW9uPW5hLm9taXQsd2VpZ2h0cz0gdmFyUG93ZXIoKSkgICAgICAgCgppbnRlcnZhbHMobW9kZWwwMikKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKKHRldGFbM109YXR0cmlidXRlcyhsb2dMaWsobW9kZWwwMikpJGRmKQooQUljY1szXT0tMiptb2RlbDAyJGxvZ0xpaysyKnRldGFbM10rKDIqdGV0YVszXSoodGV0YVszXSsxKSkvKG5yb3coZGFkb3NnKS10ZXRhWzNdLTEpKQpgYGAKCiMjIyMjICBMb2dpc3RpYyBtb2RlbCB3aXRoIGNsYXNzIG9mIGZpeGVkIGVmZmVjdCwgaW4gb3RoZXIgd29yZHMsIHdlIHdpbGwgZml0IGEgcGFyYW1ldGVyIGZvciBlYWNoIHNleCwgcGx1cyAoY29yciA9IG1vZGVsaW5nIGFuaW1hbCByZXBlYXRlZCBtZWFzdXJlcywgQ0FSMSA9IGNvbnRpbm91cyBhdXRvcmVncmVzc2l2ZSkKYGBge3J9CnBhcmY9YygpCmZvciAoaSBpbiAxOmxlbmd0aChzdW1tYXJ5KG1vZGVsMDEpJCBjb2VmZmljaWVudHMkZml4ZWQpKSAgICAgIHsKcGFyMT1zdW1tYXJ5KG1vZGVsMDEpJCBjb2VmZmljaWVudHMkZml4ZWRbW2ldXQpwYXJmPXJiaW5kKHBhcmYscGFyMSkKIH0gI3N0YXJ0aW5nIHZhbHVlcyBiYXNlZCBvbiB0aGUgbW9kZWwwMSBvdXRwdXQsIGVhY2ggY2xhc3MgaGFzIGl0cyBvd24gc3RhcnQgdmFsdWUKCm1vZGVsMDM9bmxtZSh3fmEvKDErYiooZXhwKC1rKmFnZSkpKSxmaXhlZD1saXN0KGErYitrfmNsYXNzLTEpLAogICAgICAgICAgICAgcmFuZG9tPXBkRGlhZyhhK2Ira34xKSxjb250cm9sPW5sbWVDb250cm9sKG1heEl0ZXI9MTAwKSwKICAgICAgICAgICAgIGRhdGE9ZGFkb3NnLHN0YXJ0PXBhcmYsbmEuYWN0aW9uPW5hLm9taXQsY29ycj1jb3JDQVIxKCkpCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCih0ZXRhWzRdPWF0dHJpYnV0ZXMobG9nTGlrKG1vZGVsMDMpKSRkZikKKEFJY2NbNF09LTIqbW9kZWwwMyRsb2dMaWsrMip0ZXRhWzRdKygyKnRldGFbNF0qKHRldGFbNF0rMSkpLyhucm93KGRhZG9zZyktdGV0YVs0XS0xKSkKYGBgCgojIyMjIyBGdWxsIExvZ2lzdGljIG1vZGVsIHdpdGggY2xhc3Mgb2YgZml4ZWQgZWZmZWN0LCBoZXRlcm9nZW5lb3VzIHZhcmlhbmNlIGFsb25nIHRoZSB0aW1lLCByZXBlYXRlZCBtZWFzdXJlcwpgYGB7cn0KcGFyZj1jKCkKZm9yIChpIGluIDE6bGVuZ3RoKHN1bW1hcnkobW9kZWwwMykkIGNvZWZmaWNpZW50cyRmaXhlZCkpICAgICAgewpwYXIxPXN1bW1hcnkobW9kZWwwMykkIGNvZWZmaWNpZW50cyRmaXhlZFtbaV1dCnBhcmY9cmJpbmQocGFyZixwYXIxKQogfQoKbW9kZWwwND1ubG1lKHd+YS8oMStiKihleHAoLWsqYWdlKSkpLGZpeGVkPWxpc3QoYStiK2t+Y2xhc3MtMSksCiAgICAgICAgICAgICByYW5kb209cGREaWFnKGF+MSksY29udHJvbD1ubG1lQ29udHJvbChtaW5TY2FsZT0xMCoqLTEwMCxtYXhJdGVyPTUwMCksCiAgICAgICAgICAgICBkYXRhPWRhZG9zZyxzdGFydD1wYXJmLG5hLmFjdGlvbj1uYS5vbWl0LGNvcnI9Y29yQ0FSMSgpLHdlaWdodHM9IHZhclBvd2VyKCkpICAgICAgICNsb2dpc3RpY28KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKKHRldGFbNV09YXR0cmlidXRlcyhsb2dMaWsobW9kZWwwNCkpJGRmKQooQUljY1s1XT0tMiptb2RlbDA0JGxvZ0xpaysyKnRldGFbNV0rKDIqdGV0YVs1XSoodGV0YVs1XSsxKSkvKG5yb3coZGFkb3NnKS10ZXRhWzVdLTEpKQpgYGAKCiMjIyMjIEdvbXBlcnR6IG1vZGVsLCB3aXRob3V0IGZpeGVkIGVmZmVjdApgYGB7cn0KCm1vZGVsMDU9bmxtZSh3fmEqZXhwKC1iKihleHAoLWsqYWdlKSkpLGZpeGVkPWxpc3QoYStiK2t+MSkscmFuZG9tPXBkRGlhZyhhfjEpLAogICAgICAgICAgICAgY29udHJvbD1ubG1lQ29udHJvbChtYXhJdGVyPTEwMCksCiAgICAgICAgICAgICBkYXRhPWRhZG9zZywKICAgICAgICAgICAgIHN0YXJ0PWxpc3QoZml4ZWQgPWMoMzAuNDQ0Mzg4MiwxMi43ODY3MjgxLDAuMDYpKSxuYS5hY3Rpb249bmEub21pdCkgICAgICAgCgoodGV0YVs2XT1hdHRyaWJ1dGVzKGxvZ0xpayhtb2RlbDA1KSkkZGYpCihBSWNjWzZdPS0yKm1vZGVsMDUkbG9nTGlrKzIqdGV0YVs2XSsoMip0ZXRhWzZdKih0ZXRhWzZdKzEpKS8obnJvdyhkYWRvc2cpLXRldGFbNl0tMSkpCmBgYAoKIyMjIyMgIEdvbXBlcnR6IG1vZGVsIHdpdGggY2xhc3Mgb2YgZml4ZWQgZWZmZWN0LCBpbiBvdGhlciB3b3Jkcywgd2Ugd2lsbCBmaXQgYSBwYXJhbWV0ZXIgZm9yIGVhY2ggc2V4CmBgYHtyfQpzdDA1PWMocmVwKHN1bW1hcnkobW9kZWwwNSkkIGNvZWZmaWNpZW50cyRmaXhlZFtbMV1dLGxlbmd0aChsZXZlbHMoZGFkb3NnJGNsYXNzKSkpLHJlcChzdW1tYXJ5KG1vZGVsMDUpJCBjb2VmZmljaWVudHMkZml4ZWRbWzJdXSxsZW5ndGgobGV2ZWxzKGRhZG9zZyRjbGFzcykpKSxyZXAoc3VtbWFyeShtb2RlbDA1KSQgY29lZmZpY2llbnRzJGZpeGVkW1szXV0sbGVuZ3RoKGxldmVscyhkYWRvc2ckY2xhc3MpKSkpICNzdGFydGluZyB2YWx1ZXMgYmFzZWQgb24gdGhlIG1vZGVsMDUgb3V0cHV0Cgptb2RlbDA2PW5sbWUod35hKmV4cCgtYiooZXhwKC1rKmFnZSkpKSxmaXhlZD1saXN0KGErYitrfmNsYXNzLTEpLAogICAgICAgICAgICAgcmFuZG9tPXBkRGlhZyhhK2Ira34xKSxjb250cm9sPW5sbWVDb250cm9sKG1heEl0ZXI9MTAwKSwKICAgICAgICAgICAgIGRhdGE9ZGFkb3NnLHN0YXJ0PXN0MDEsbmEuYWN0aW9uPW5hLm9taXQpCgoKKHRldGFbN109YXR0cmlidXRlcyhsb2dMaWsobW9kZWwwNikpJGRmKQooQUljY1s3XT0tMiptb2RlbDA2JGxvZ0xpaysyKnRldGFbN10rKDIqdGV0YVs3XSoodGV0YVs3XSsxKSkvKG5yb3coZGFkb3NnKS10ZXRhWzddLTEpKQpgYGAKCiMjIyMjICBHb21wZXJ0eiBtb2RlbCB3aXRoIGNsYXNzIG9mIGZpeGVkIGVmZmVjdCwgaW4gb3RoZXIgd29yZHMsIHdlIHdpbGwgZml0IGEgcGFyYW1ldGVyIGZvciBlYWNoIHNleCwgcGx1cyAod2VpZ2h0cyA9IGhldGVyb2dlbmVvdXMgdmFyaWFuY2UgYWxvbmcgdGhlIHRpbWUsIHZhclBvd2VyPSBwb3dlciB2YXJpYW5jZSBmdW5jdGlvbiBpcyBkZWZpbmVkIGFzIHMyKHYpID0gfHZ8XigyKnQpKQoKYGBge3J9CnBhcmY9YygpCmZvciAoaSBpbiAxOmxlbmd0aChzdW1tYXJ5KG1vZGVsMDYpJCBjb2VmZmljaWVudHMkZml4ZWQpKSAgICAgIHsKcGFyMT1zdW1tYXJ5KG1vZGVsMDYpJCBjb2VmZmljaWVudHMkZml4ZWRbW2ldXQpwYXJmPXJiaW5kKHBhcmYscGFyMSkKIH0gI3N0YXJ0aW5nIHZhbHVlcyBiYXNlZCBvbiB0aGUgbW9kZWwwMSBvdXRwdXQsIGVhY2ggY2xhc3MgaGFzIGl0cyBvd24gc3RhcnQgdmFsdWUKCm1vZGVsMDc9bmxtZSh3fmEqZXhwKC1iKihleHAoLWsqYWdlKSkpLGZpeGVkPWxpc3QoYStiK2t+Y2xhc3MtMSksCiAgICAgICAgICAgICByYW5kb209cGREaWFnKGErYitrfjEpLGNvbnRyb2w9bmxtZUNvbnRyb2wobWF4SXRlcj0xMDApLAogICAgICAgICAgICAgZGF0YT1kYWRvc2csc3RhcnQ9cGFyZixuYS5hY3Rpb249bmEub21pdCx3ZWlnaHRzPSB2YXJQb3dlcigpKSAgICAgICAKCmludGVydmFscyhtb2RlbDA3KQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgoodGV0YVs4XT1hdHRyaWJ1dGVzKGxvZ0xpayhtb2RlbDA3KSkkZGYpCihBSWNjWzhdPS0yKm1vZGVsMDckbG9nTGlrKzIqdGV0YVs4XSsoMip0ZXRhWzhdKih0ZXRhWzhdKzEpKS8obnJvdyhkYWRvc2cpLXRldGFbOF0tMSkpCmBgYAoKIyMjIyMgIEdvbXBlcnR6IG1vZGVsIHdpdGggY2xhc3Mgb2YgZml4ZWQgZWZmZWN0LCBpbiBvdGhlciB3b3Jkcywgd2Ugd2lsbCBmaXQgYSBwYXJhbWV0ZXIgZm9yIGVhY2ggc2V4LCBwbHVzIChjb3JyID0gbW9kZWxpbmcgYW5pbWFsIHJlcGVhdGVkIG1lYXN1cmVzLCBDQVIxID0gY29udGlub3VzIGF1dG9yZWdyZXNzaXZlKQpgYGB7cn0KcGFyZj1jKCkKZm9yIChpIGluIDE6bGVuZ3RoKHN1bW1hcnkobW9kZWwwNikkIGNvZWZmaWNpZW50cyRmaXhlZCkpICAgICAgewpwYXIxPXN1bW1hcnkobW9kZWwwNikkIGNvZWZmaWNpZW50cyRmaXhlZFtbaV1dCnBhcmY9cmJpbmQocGFyZixwYXIxKQogfSAjc3RhcnRpbmcgdmFsdWVzIGJhc2VkIG9uIHRoZSBtb2RlbDAxIG91dHB1dCwgZWFjaCBjbGFzcyBoYXMgaXRzIG93biBzdGFydCB2YWx1ZQoKbW9kZWwwOD1ubG1lKHd+YSpleHAoLWIqKGV4cCgtayphZ2UpKSksZml4ZWQ9bGlzdChhK2Ira35jbGFzcy0xKSwKICAgICAgICAgICAgIHJhbmRvbT1wZERpYWcoYStiK2t+MSksY29udHJvbD1ubG1lQ29udHJvbChtYXhJdGVyPTEwMCksCiAgICAgICAgICAgICBkYXRhPWRhZG9zZyxzdGFydD1wYXJmLG5hLmFjdGlvbj1uYS5vbWl0LGNvcnI9Y29yQ0FSMSgpKQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgoodGV0YVs5XT1hdHRyaWJ1dGVzKGxvZ0xpayhtb2RlbDA4KSkkZGYpCihBSWNjWzldPS0yKm1vZGVsMDgkbG9nTGlrKzIqdGV0YVs5XSsoMip0ZXRhWzldKih0ZXRhWzldKzEpKS8obnJvdyhkYWRvc2cpLXRldGFbOV0tMSkpCmBgYAoKIyMjIyMgRnVsbCBHb21wZXJ0eiBtb2RlbCB3aXRoIGNsYXNzIG9mIGZpeGVkIGVmZmVjdCwgaGV0ZXJvZ2VuZW91cyB2YXJpYW5jZSBhbG9uZyB0aGUgdGltZSwgcmVwZWF0ZWQgbWVhc3VyZXMKYGBge3J9CnBhcmY9YygpCmZvciAoaSBpbiAxOmxlbmd0aChzdW1tYXJ5KG1vZGVsMDYpJCBjb2VmZmljaWVudHMkZml4ZWQpKSAgICAgIHsKcGFyMT1zdW1tYXJ5KG1vZGVsMDYpJCBjb2VmZmljaWVudHMkZml4ZWRbW2ldXQpwYXJmPXJiaW5kKHBhcmYscGFyMSkKIH0gI3N0YXJ0aW5nIHZhbHVlcyBiYXNlZCBvbiB0aGUgbW9kZWwwMSBvdXRwdXQsIGVhY2ggY2xhc3MgaGFzIGl0cyBvd24gc3RhcnQgdmFsdWUKCm1vZGVsMDk9bmxtZSh3fmEqZXhwKC1iKihleHAoLWsqYWdlKSkpLGZpeGVkPWxpc3QoYStiK2t+Y2xhc3MtMSksCiAgICAgICAgICAgICByYW5kb209cGREaWFnKGEra34xKSxjb250cm9sPW5sbWVDb250cm9sKG1pblNjYWxlPTEwKiotMTAwLG1heEl0ZXI9NTAwKSwKICAgICAgICAgICAgIGRhdGE9ZGFkb3NnLHN0YXJ0PXBhcmYsbmEuYWN0aW9uPW5hLm9taXQsY29ycj1jb3JDQVIxKCksd2VpZ2h0cz0gdmFyUG93ZXIoKSkKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKKHRldGFbMTBdPWF0dHJpYnV0ZXMobG9nTGlrKG1vZGVsMDkpKSRkZikKKEFJY2NbMTBdPS0yKm1vZGVsMDkkbG9nTGlrKzIqdGV0YVsxMF0rKDIqdGV0YVsxMF0qKHRldGFbMTBdKzEpKS8obnJvdyhkYWRvc2cpLXRldGFbMTBdLTEpKQpgYGAKCiMjIyMjIG11bHRpLW1vZGVsIHNlbGVjdGlvbiBmcmFtZXdvcmsgKEJ1cm5oYW0gYW5kIEFuZGVyc29uLCAyMDA0KQpgYGB7cn0KCmRlbHRhPWMoKQpmb3IoaSBpbiAxOiBsZW5ndGgoQUljYykpewpkZWx0YVtpXT1BSWNjW2ldLW1pbihBSWNjKQp9Cgp3cHJvPWMoKQpmb3IoaSBpbiAxOmxlbmd0aChBSWNjKSl7Cndwcm9baV09ZXhwKC1kZWx0YVtpXS8yKQp9CnN1bSh3cHJvWzE6bGVuZ3RoKEFJY2MpXSkKCndwcm9iPWMoKQpmb3IoaSBpbiAxOiBsZW5ndGgoQUljYykpewp3cHJvYltpXT1leHAoLWRlbHRhW2ldLzIpL3N1bSh3cHJvWzE6bGVuZ3RoKEFJY2MpXSkKfQoKCkVSPWMoKQpmb3IoaSBpbiAxOiBsZW5ndGgoQUljYykpewpFUltpXT1tYXgod3Byb2IpL3dwcm9iW2ldCn0KCihxdWFkcm8uYWthaWtlPWRhdGEuZnJhbWUodGV0YSxBSUNjPUFJY2MsZGVsdGEsd3Byb2IsRVIpKQpgYGAKCgojIyMjIyBFeHRyYWN0aW5nIHRoZSBjb25maWRlbmNlIGludGVydmFscyBvZiB0aGUgcGFyYW1ldGVycyBhZnRlciBjaG9vc2luZyB0aGUgYmVzdCBtb2RlbApgYGB7cn0KaW50ZXJ2YWxzKG1vZGVsMDkpCmBgYAoKIyMjIyMgUHJlZGljdGlvbiBhbmQgR3Jvd3RoIHJhdGUgZnVuY3Rpb24KYGBge3J9CkdvbXBlcnR6aGF0PWZ1bmN0aW9uKGEsYixrLGFnZSl7CiAgeT1hKmV4cCgtYiooZXhwKC1rKmFnZSkpKQogIHJldHVybih5KQp9CkdSR29tcGVydHo9ZnVuY3Rpb24oYSxiLGssYWdlKXsKICB5PWEgKiAoZXhwKC1iICogKGV4cCgtayAqIGFnZSkpKSAqIChiICogKGV4cCgtayAqIGFnZSkgKiBrKSkpCiAgcmV0dXJuKHkpCn0KYGBgCgoKIyMjIyMgY3JlYXRpbmcgaW50ZXJhY3RpdmUgZ3JhcGhpY3Mgd2l0aCBwbG90bHkKYGBge3J9CnNldHdkKCJ+L0luc3luYy9sZW9uYXJkb2dsb3JpYUB1ZW5mLmJyL0dvb2dsZSBEcml2ZS9NRU5UT1JJQV9SL01vbmRheV9tZWV0aW5nIikKcGFyYW09cmVhZC50YWJsZSgicGFyYW1ldGVycy50eHQiLGg9VCkKcm93Lm5hbWVzKHBhcmFtKT1jKCJNYWxlcyIsIkZlbWFsZXMiKQoKdGltZT1zZXEoMSw5MCxieT0wLjI1KQojcHJlZGljdGlvbnMKcHJlZGMxPUdvbXBlcnR6aGF0KGE9cGFyYW1bMSwxXSxiPXBhcmFtWzEsMl0saz1wYXJhbVsxLDNdLGFnZSA9dGltZSkKcHJlZGMyPUdvbXBlcnR6aGF0KGE9cGFyYW1bMiwxXSxiPXBhcmFtWzIsMl0saz1wYXJhbVsyLDNdLGFnZSA9dGltZSkKCiNncm93dGggcmF0ZQpncmMxPUdSR29tcGVydHooYT1wYXJhbVsxLDFdLGI9cGFyYW1bMSwyXSxrPXBhcmFtWzEsM10sYWdlID10aW1lKQpncmMyPUdSR29tcGVydHooYT1wYXJhbVsyLDFdLGI9cGFyYW1bMiwyXSxrPXBhcmFtWzIsM10sYWdlID10aW1lKQoKI2luZmxhY3Rpb24gcG9pbnQgKG1heCBlZmZpY2llbmN5KQptYXhlZjE9ZGF0YS5mcmFtZShncmMxKQpyb3duYW1lcyhtYXhlZjEpPXRpbWUKCm1heGVmeDE9YXMubnVtZXJpYyhyb3duYW1lcyhtYXhlZjEpW3doaWNoLm1heChtYXhlZjEkZ3JjMSldKQptYXhlZnkxPW1heGVmMSRncmMxW3doaWNoLm1heChtYXhlZjEkZ3JjMSldCgptYXhlZjI9ZGF0YS5mcmFtZShncmMyKQpyb3duYW1lcyhtYXhlZjIpPXRpbWUKCm1heGVmeDI9YXMubnVtZXJpYyhyb3duYW1lcyhtYXhlZjIpW3doaWNoLm1heChtYXhlZjIkZ3JjMildKQptYXhlZnkyPW1heGVmMiRncmMyW3doaWNoLm1heChtYXhlZjIkZ3JjMildCgojIyMgTXVsdGlwbGUgWSBBeGVzCgpheSA8LSBsaXN0KAogIHRpY2tmb250ID0gbGlzdChjb2xvciA9ICJibGFjayIpLAogIG92ZXJsYXlpbmcgPSAieSIsCiAgc2lkZSA9ICJyaWdodCIsCiAgdGl0bGUgPSAiRGFpbHkgV2VpZ2h0IEdhaW4oZykiCikKZmlnIDwtIHBsb3RfbHkoKQpmaWcgPC0gZmlnICU+JSBhZGRfbGluZXMoeCA9IHRpbWUsIHkgPSBwcmVkYzEsIG5hbWUgPSAiV2VpZ2h0IE1hbGVzIikKZmlnIDwtIGZpZyAlPiUgYWRkX2xpbmVzKHggPSB0aW1lLCB5ID0gZ3JjMSwgbmFtZSA9ICJHcm93dGggUmF0ZSBNYWxlcyIsIHlheGlzID0gInkyIikKZmlnIDwtIGZpZyAlPiUgYWRkX21hcmtlcnMoeCA9IG1heGVmeDEsIHkgPSBtYXhlZnkxLCBuYW1lID0gIk1heCBHcm93dGggUmF0ZSBNYWxlcyIsIHlheGlzID0gInkyIikKZmlnIDwtIGZpZyAlPiUgYWRkX3NlZ21lbnRzKGxpbmU9bGlzdCh0eXBlPSJkYXNoIikseCA9IG1heGVmeDEseGVuZD1tYXhlZngxLCB5ID0gMCx5ZW5kID1tYXhlZnkxLCB5YXhpcyA9ICJ5MiIsbmFtZSA9ICJNYXggR3Jvd3RoIFJhdGUgTWFsZXMiKQoKZmlnIDwtIGZpZyAlPiUgYWRkX2xpbmVzKHggPSB0aW1lLCB5ID0gcHJlZGMyLCBuYW1lID0gIldlaWdodCBGZW1hbGVzIikKZmlnIDwtIGZpZyAlPiUgYWRkX2xpbmVzKHggPSB0aW1lLCB5ID0gZ3JjMiwgbmFtZSA9ICJHcm93dGggUmF0ZSBGZW1hbGVzIiwgeWF4aXMgPSAieTIiKQpmaWcgPC0gZmlnICU+JSBhZGRfbWFya2Vycyh4ID0gbWF4ZWZ4MiwgeSA9IG1heGVmeTIsIG5hbWUgPSAiTWF4IEdyb3d0aCBSYXRlIEZlbWFsZXMiLCB5YXhpcyA9ICJ5MiIpCmZpZyA8LSBmaWcgJT4lIGFkZF9zZWdtZW50cyhjb2xvciA9IEkoImdyYXkiKSx4ID0gbWF4ZWZ4Mix4ZW5kPW1heGVmeDIsIHkgPSAwLHllbmQgPW1heGVmeTIsIHlheGlzID0gInkyIixuYW1lID0gIk1heCBHcm93dGggUmF0ZSBGZW1hbGVzIikKCmZpZyA8LSBmaWcgJT4lIGxheW91dCgKICB0aXRsZSA9ICJHcm93dGggQ3VydmUgd2l0aCBHcm93dGggcmF0ZSIsIHlheGlzMiA9IGF5LAogIHhheGlzID0gbGlzdCh0aXRsZT0iQWdlKGRheXMpIiksCiAgeWF4aXMgPSBsaXN0KHRpdGxlPSJXZWlnaHQoZykiKQopCmZpZwpgYGAKCgoKIyMjIyMgSW1wb3J0aW5nIE1pbGtpbmcgRGF0YXNldAoKIyMgSW1wb3J0aW5nIGRhdGFzZXQgZnJvbSB0eHQKCmBgYHtyfQpzZXR3ZCgifi9JbnN5bmMvbGVvbmFyZG9nbG9yaWFAdWVuZi5ici9Hb29nbGUgRHJpdmUvTUVOVE9SSUFfUi9Nb25kYXlfbWVldGluZyIpCmRhdGFfbWlsa2cgPC0gcmVhZC50YWJsZSgiZGF0YV9taWxrLnR4dCIsIGggPSBUKSAlPiUgCiAgbXV0YXRlKGFuaW1hbD1mYWN0b3IoYW5pbWFsKSxDRz1mYWN0b3IoQ0cpKSAlPiUgCiAgZ3JvdXBlZERhdGEoTVl+RElNfGFuaW1hbCxkYXRhPS4pICNDcmVhdGluZyBkYXRhc2V0IGluZGljYXRpbmcgYW5pbWFsIHJlcGVhdGVkIG1lYXN1cmVzIGJ5IGRheXMgaW4gbWlsawpgYGAKCiMjIyMjIFdPT0QgbW9kZWwsIHdpdGhvdXQgZml4ZWQgZWZmZWN0CmBgYHtyfQp0ZXRhPWMoKTsgICBBSWNjPWMoKTsKCm1vZGVsMDA9bmxtZShNWX5hKihESU0qKmIpKmV4cCgtaypESU0pLGZpeGVkPWxpc3QoYStiK2t+MSkscmFuZG9tPXBkRGlhZyhhK2J+MSksCiAgICAgICAgICAgICBjb250cm9sPW5sbWVDb250cm9sKG1heEl0ZXI9MTAwKSwKICAgICAgICAgICAgIGRhdGE9ZGF0YV9taWxrZywKICAgICAgICAgICAgIHN0YXJ0PWxpc3QoZml4ZWQgPWMoMjAsMC4xLDAuMDAzKSksbmEuYWN0aW9uPW5hLm9taXQpICAgICAgIAoKKHRldGFbMV09YXR0cmlidXRlcyhsb2dMaWsobW9kZWwwMCkpJGRmKQooQUljY1sxXT0tMiptb2RlbDAwJGxvZ0xpaysyKnRldGFbMV0rKDIqdGV0YVsxXSoodGV0YVsxXSsxKSkvKG5yb3coZGF0YV9taWxrZyktdGV0YVsxXS0xKSkKYGBgCgojIyMjIyAgV09PRCBtb2RlbCB3aXRoIGNsYXNzIG9mIGZpeGVkIGVmZmVjdCwgaW4gb3RoZXIgd29yZHMsIHdlIHdpbGwgZml0IGEgcGFyYW1ldGVyIGZvciBlYWNoIHNleApgYGB7cn0Kc3QwMT1jKHJlcChzdW1tYXJ5KG1vZGVsMDApJCBjb2VmZmljaWVudHMkZml4ZWRbWzFdXSxsZW5ndGgobGV2ZWxzKGRhdGFfbWlsa2ckQ0cpKSkscmVwKHN1bW1hcnkobW9kZWwwMCkkIGNvZWZmaWNpZW50cyRmaXhlZFtbMl1dLGxlbmd0aChsZXZlbHMoZGF0YV9taWxrZyRDRykpKSxyZXAoc3VtbWFyeShtb2RlbDAwKSQgY29lZmZpY2llbnRzJGZpeGVkW1szXV0sbGVuZ3RoKGxldmVscyhkYXRhX21pbGtnJENHKSkpKSAjc3RhcnRpbmcgdmFsdWVzIGJhc2VkIG9uIHRoZSBtb2RlbDAwIG91dHB1dAoKbW9kZWwwMT1ubG1lKE1ZfmEqKERJTSoqYikqZXhwKC1rKkRJTSksZml4ZWQ9bGlzdChhK2Ira35DRy0xKSxyYW5kb209cGREaWFnKGF+MSksCiAgICAgICAgICAgICBjb250cm9sPW5sbWVDb250cm9sKG1heEl0ZXI9MTAwKSwKICAgICAgICAgICAgIGRhdGE9ZGF0YV9taWxrZyxzdGFydD1zdDAxLG5hLmFjdGlvbj1uYS5vbWl0KQoKCih0ZXRhWzJdPWF0dHJpYnV0ZXMobG9nTGlrKG1vZGVsMDEpKSRkZikKKEFJY2NbMl09LTIqbW9kZWwwMSRsb2dMaWsrMip0ZXRhWzJdKygyKnRldGFbMl0qKHRldGFbMl0rMSkpLyhucm93KGRhdGFfbWlsa2cpLXRldGFbMl0tMSkpCmBgYAoKIyMjIyMgIFdPT0QgbW9kZWwgd2l0aCBjbGFzcyBvZiBmaXhlZCBlZmZlY3QsIGluIG90aGVyIHdvcmRzLCB3ZSB3aWxsIGZpdCBhIHBhcmFtZXRlciBmb3IgZWFjaCBzZXgsIHBsdXMgKHdlaWdodHMgPSBoZXRlcm9nZW5lb3VzIHZhcmlhbmNlIGFsb25nIHRoZSB0aW1lLCB2YXJQb3dlcj0gcG93ZXIgdmFyaWFuY2UgZnVuY3Rpb24gaXMgZGVmaW5lZCBhcyBzMih2KSA9IHx2fF4oMip0KSkKCmBgYHtyfQpwYXJmPWMoKQpmb3IgKGkgaW4gMTpsZW5ndGgoc3VtbWFyeShtb2RlbDAxKSQgY29lZmZpY2llbnRzJGZpeGVkKSkgICAgICB7CnBhcjE9c3VtbWFyeShtb2RlbDAxKSQgY29lZmZpY2llbnRzJGZpeGVkW1tpXV0KcGFyZj1yYmluZChwYXJmLHBhcjEpCiB9ICNzdGFydGluZyB2YWx1ZXMgYmFzZWQgb24gdGhlIG1vZGVsMDEgb3V0cHV0LCBlYWNoIGNsYXNzIGhhcyBpdHMgb3duIHN0YXJ0IHZhbHVlCgptb2RlbDAyPW5sbWUoTVl+YSooRElNKipiKSpleHAoLWsqRElNKSxmaXhlZD1saXN0KGErYitrfkNHLTEpLHJhbmRvbT1wZERpYWcoYX4xKSwKICAgICAgICAgICAgIGNvbnRyb2w9bmxtZUNvbnRyb2wobWF4SXRlcj01MDApLAogICAgICAgICAgICAgZGF0YT1kYXRhX21pbGtnLHN0YXJ0PXBhcmYsbmEuYWN0aW9uPW5hLm9taXQsd2VpZ2h0cz0gdmFyUG93ZXIoKSkgICAgICAgCgppbnRlcnZhbHMobW9kZWwwMikKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKKHRldGFbM109YXR0cmlidXRlcyhsb2dMaWsobW9kZWwwMikpJGRmKQooQUljY1szXT0tMiptb2RlbDAyJGxvZ0xpaysyKnRldGFbM10rKDIqdGV0YVszXSoodGV0YVszXSsxKSkvKG5yb3coZGF0YV9taWxrZyktdGV0YVszXS0xKSkKYGBgCgojIyMjIyAgV09PRCBtb2RlbCB3aXRoIGNsYXNzIG9mIGZpeGVkIGVmZmVjdCwgaW4gb3RoZXIgd29yZHMsIHdlIHdpbGwgZml0IGEgcGFyYW1ldGVyIGZvciBlYWNoIHNleCwgcGx1cyAoY29yciA9IG1vZGVsaW5nIGFuaW1hbCByZXBlYXRlZCBtZWFzdXJlcywgQ0FSMSA9IGNvbnRpbm91cyBhdXRvcmVncmVzc2l2ZSkKYGBge3J9CnBhcmY9YygpCmZvciAoaSBpbiAxOmxlbmd0aChzdW1tYXJ5KG1vZGVsMDEpJCBjb2VmZmljaWVudHMkZml4ZWQpKSAgICAgIHsKcGFyMT1zdW1tYXJ5KG1vZGVsMDEpJCBjb2VmZmljaWVudHMkZml4ZWRbW2ldXQpwYXJmPXJiaW5kKHBhcmYscGFyMSkKIH0gI3N0YXJ0aW5nIHZhbHVlcyBiYXNlZCBvbiB0aGUgbW9kZWwwMSBvdXRwdXQsIGVhY2ggY2xhc3MgaGFzIGl0cyBvd24gc3RhcnQgdmFsdWUKCm1vZGVsMDM9bmxtZShNWX5hKihESU0qKmIpKmV4cCgtaypESU0pLGZpeGVkPWxpc3QoYStiK2t+Q0ctMSkscmFuZG9tPXBkRGlhZyhhfjEpLAogICAgICAgICAgICAgY29udHJvbD1ubG1lQ29udHJvbChtYXhJdGVyPTEwMCksCiAgICAgICAgICAgICBkYXRhPWRhdGFfbWlsa2csc3RhcnQ9cGFyZixuYS5hY3Rpb249bmEub21pdCxjb3JyPWNvckNBUjEoKSkKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKKHRldGFbNF09YXR0cmlidXRlcyhsb2dMaWsobW9kZWwwMykpJGRmKQooQUljY1s0XT0tMiptb2RlbDAzJGxvZ0xpaysyKnRldGFbNF0rKDIqdGV0YVs0XSoodGV0YVs0XSsxKSkvKG5yb3coZGF0YV9taWxrZyktdGV0YVs0XS0xKSkKYGBgCgojIyMjIyBGdWxsIFdPT0QgbW9kZWwgd2l0aCBjbGFzcyBvZiBmaXhlZCBlZmZlY3QsIGhldGVyb2dlbmVvdXMgdmFyaWFuY2UgYWxvbmcgdGhlIHRpbWUsIHJlcGVhdGVkIG1lYXN1cmVzCmBgYHtyfQpwYXJmPWMoKQpmb3IgKGkgaW4gMTpsZW5ndGgoc3VtbWFyeShtb2RlbDAzKSQgY29lZmZpY2llbnRzJGZpeGVkKSkgICAgICB7CnBhcjE9c3VtbWFyeShtb2RlbDAzKSQgY29lZmZpY2llbnRzJGZpeGVkW1tpXV0KcGFyZj1yYmluZChwYXJmLHBhcjEpCiB9Cgptb2RlbDA0PW5sbWUoTVl+YSooRElNKipiKSpleHAoLWsqRElNKSxmaXhlZD1saXN0KGErYitrfkNHLTEpLHJhbmRvbT1wZERpYWcoYStiK2t+MSksCiAgICAgICAgICAgICBjb250cm9sPW5sbWVDb250cm9sKG1heEl0ZXI9MTAwKSwKICAgICAgICAgICAgIGRhdGE9ZGF0YV9taWxrZyxzdGFydD1wYXJmLG5hLmFjdGlvbj1uYS5vbWl0LGNvcnI9Y29yQ0FSMSgpLHdlaWdodHM9IHZhclBvd2VyKCkpIAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgoodGV0YVs1XT1hdHRyaWJ1dGVzKGxvZ0xpayhtb2RlbDA0KSkkZGYpCihBSWNjWzVdPS0yKm1vZGVsMDQkbG9nTGlrKzIqdGV0YVs1XSsoMip0ZXRhWzVdKih0ZXRhWzVdKzEpKS8obnJvdyhkYXRhX21pbGtnKS10ZXRhWzVdLTEpKQpgYGAKCiMjIyMjIG11bHRpLW1vZGVsIHNlbGVjdGlvbiBmcmFtZXdvcmsgKEJ1cm5oYW0gYW5kIEFuZGVyc29uLCAyMDA0KQpgYGB7cn0KCmRlbHRhPWMoKQpmb3IoaSBpbiAxOiBsZW5ndGgoQUljYykpewpkZWx0YVtpXT1BSWNjW2ldLW1pbihBSWNjKQp9Cgp3cHJvPWMoKQpmb3IoaSBpbiAxOmxlbmd0aChBSWNjKSl7Cndwcm9baV09ZXhwKC1kZWx0YVtpXS8yKQp9CnN1bSh3cHJvWzE6bGVuZ3RoKEFJY2MpXSkKCndwcm9iPWMoKQpmb3IoaSBpbiAxOiBsZW5ndGgoQUljYykpewp3cHJvYltpXT1leHAoLWRlbHRhW2ldLzIpL3N1bSh3cHJvWzE6bGVuZ3RoKEFJY2MpXSkKfQoKCkVSPWMoKQpmb3IoaSBpbiAxOiBsZW5ndGgoQUljYykpewpFUltpXT1tYXgod3Byb2IpL3dwcm9iW2ldCn0KCihxdWFkcm8uYWthaWtlPWRhdGEuZnJhbWUodGV0YSxBSUNjPUFJY2MsZGVsdGEsd3Byb2IsRVIpKQpgYGAKCgojIyMjIyBFeHRyYWN0aW5nIHRoZSBjb25maWRlbmNlIGludGVydmFscyBvZiB0aGUgcGFyYW1ldGVycyBhZnRlciBjaG9vc2luZyB0aGUgYmVzdCBtb2RlbApgYGB7cn0KaW50ZXJ2YWxzKG1vZGVsMDQpCmBgYAoKIyMjIyMgUHJlZGljdGlvbiBNWSBhbmQgVE1ZCmBgYHtyfQpNV29vZGhhdD1mdW5jdGlvbihhLGIsayxhZ2UpewogIHk9KGEqYWdlXmIpKmV4cCgtayphZ2UpCiAgcmV0dXJuKHkpCn0KCiNtaWxrIHllbGQgd2l0aCBXb29kLCBNWWwgPSBmaXJzdCBkYXksTVl1PWxhc3QgZGF5Ck1ZV29vZD1mdW5jdGlvbihhLGIsayxNWWwsTVl1KXsKICBpbnQ9YygpCiAgZm9yKGkgaW4gMTpsZW5ndGgoYSkpewogICAgaW50ZWdyYW5kIDwtIGZ1bmN0aW9uKGFnZSkge2FbaV0qKGFnZSoqYltpXSkqZXhwKC1rW2ldKmFnZSl9CiAgICBpbnQxIDwtIHRyeShpbnRlZ3JhdGUoaW50ZWdyYW5kLCBsb3dlcj1NWWwsIHVwcGVyPU1ZdSksIHNpbGVudCA9IFRSVUUpCiAgICBpZihpbmhlcml0cyhpbnQgLCd0cnktZXJyb3InKSl7CiAgICAgIHdhcm5pbmcoYXMudmVjdG9yKGludCkpCiAgICAgIGludGVncmF0ZWQgPC0gTkFfcmVhbF8KICAgIH0gZWxzZSB7CiAgICAgIGludGVncmF0ZWQgPC0gaW50MVsxXQogICAgfQogICAgaW50PXJiaW5kKGludCxpbnQxWzFdKQogIH0KCiAgcmV0dXJuKGludCkKfQpgYGAKIyMjIyMgVG90YWwgTWlsayBZaWVsZAoKYGBge3J9CihUTVlDRzIyPU1ZV29vZChhPWFzLm51bWVyaWMoaW50ZXJ2YWxzKG1vZGVsMDQpJGZpeGVkWzIsMl0pLAogICAgICAgICAgICAgICAgIGI9YXMubnVtZXJpYyhpbnRlcnZhbHMobW9kZWwwNCkkZml4ZWRbOCwyXSksCiAgICAgICAgICAgICAgICAgaz1hcy5udW1lcmljKGludGVydmFscyhtb2RlbDA0KSRmaXhlZFsxNCwyXSksCiAgICAgICAgICAgICAgICAgTVlsPTEwLE1ZdT0zMDUpKQoKCihUTVlDRzMyPU1ZV29vZChhPWFzLm51bWVyaWMoaW50ZXJ2YWxzKG1vZGVsMDQpJGZpeGVkWzUsMl0pLAogICAgICAgICAgICAgICBiPWFzLm51bWVyaWMoaW50ZXJ2YWxzKG1vZGVsMDQpJGZpeGVkWzExLDJdKSwKICAgICAgICAgICAgICAgaz1hcy5udW1lcmljKGludGVydmFscyhtb2RlbDA0KSRmaXhlZFsxNywyXSksCiAgICAgICAgICAgICAgIE1ZbD0xMCxNWXU9MzA1KSkKClRNWUNHMjJbWzFdXS1UTVlDRzMyW1sxXV0KYGBgCgojIyMjIyBjcmVhdGluZyBpbnRlcmFjdGl2ZSBncmFwaGljcyB3aXRoIHBsb3RseQpgYGB7cn0KCmFnZT1zZXEoMSwzMDUsYnk9MC4yNSkKI3ByZWRpY3Rpb25zCnByZWRjMT1NV29vZGhhdChhPWFzLm51bWVyaWMoaW50ZXJ2YWxzKG1vZGVsMDQpJGZpeGVkWzIsMl0pLGI9YXMubnVtZXJpYyhpbnRlcnZhbHMobW9kZWwwNCkkZml4ZWRbOCwyXSksaz1hcy5udW1lcmljKGludGVydmFscyhtb2RlbDA0KSRmaXhlZFsxNCwyXSksYWdlPWFnZSkKcHJlZGMyPU1Xb29kaGF0KGE9YXMubnVtZXJpYyhpbnRlcnZhbHMobW9kZWwwNCkkZml4ZWRbNSwyXSksYj1hcy5udW1lcmljKGludGVydmFscyhtb2RlbDA0KSRmaXhlZFsxMSwyXSksaz1hcy5udW1lcmljKGludGVydmFscyhtb2RlbDA0KSRmaXhlZFsxNywyXSksYWdlPWFnZSkKCiMjIyBNdWx0aXBsZSBZIEF4ZXMKCiNheSA8LSBsaXN0KAojICB0aWNrZm9udCA9IGxpc3QoY29sb3IgPSAiYmxhY2siKSwKIyAgb3ZlcmxheWluZyA9ICJ5IiwKIyAgc2lkZSA9ICJyaWdodCIsCiMgIHRpdGxlID0gIkRhaWx5IFdlaWdodCBHYWluKGcpIgojKQpmaWcgPC0gcGxvdF9seSgpCmZpZyA8LSBmaWcgJT4lIGFkZF9saW5lcyh4ID0gYWdlLCB5ID0gcHJlZGMxLCBuYW1lID0gIk1pbGsgWWllbGQgQ0cyMiIpCgpmaWcgPC0gZmlnICU+JSBhZGRfbGluZXMoeCA9IGFnZSwgeSA9IHByZWRjMiwgbmFtZSA9ICJNaWxrIFlpZWxkIENHMzIiKQoKCmZpZyA8LSBmaWcgJT4lIGxheW91dCgKICB0aXRsZSA9ICJXT09EIE1pbGsgY3VydmUiLAogIHhheGlzID0gbGlzdCh0aXRsZT0iRElNKGRheXMpIiksCiAgeWF4aXMgPSBsaXN0KHRpdGxlPSJNWSAoa2cpIikKKQpmaWcKYGBgCg==